/**
 * Copyright @2016-present, Sumscope, Inc.
 * All rights reserved.
 */

class Socket {
  
  constructor(socket, listeners, middleware, _emitFilters, session) {
    this.socket = socket;
    this._middleware = null;
    this._emitFilters = null;
    this.session = session;
    this.update(listeners, middleware, _emitFilters, true);
  }
  
  on(event, handler) {
    this.socket.on(event, (data, cb) => {
      let ctx = {
        event,
        data,
        socket: this,
        ack: cb
      };
      this._handleEvent(ctx, handler);
    })
  }
  
  get id() {
    return this.socket.id;
  }
  
  emit(event, packet) {
    if (!this._emitFilters || this._doFilter(event, packet)) {
      this.socket.emit(event, packet);
    }
  }
  
  broadcast(event, packet) {
    this.socket.broadcast.emit(event, packet);
  }
  
  disconnect() {
    this.socket.disconnect();
  }
  
  join(channel) {
    this.socket.join(channel);
  }
  
  leave(channel) {
    this.socket.leave(channel);
  }
  
  update(listeners, middleware, emitFilters, forceFirst = false) {
    this.socket.removeAllListeners();
    this._middleware = middleware;
    this._emitFilters = emitFilters;
    listeners.forEach((handlers, event) => {
      if (event === 'connection' && !forceFirst) {
        return;
      }
      
      handlers.forEach(handler => {
        if (event === 'connection') {
          this._handleEvent({
            event,
            socket: this
          }, handler);
        } else {
          this.on(event, handler)
        }
      });
    })
  }
  
  _handleEvent(ctx, handler) {
    if (!this._middleware) {
      handler(ctx);
      return;
    }
    this._middleware(ctx)
      .then(() => handler(ctx));
  }
  
  _doFilter(event, packet) {
    let ctx = {socket: this, event, packet};
    for (let filter of this._emitFilters) {
      if (!filter(ctx)) {
        return false;
      }
    }
    return true;
  }
}

module.exports = Socket;
